﻿/**
* @description  Class handles loading and playback of MP3 files.
*/
package com.moke.media.controllers {

	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundMixer;
	import flash.media.SoundTransform;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.IOErrorEvent;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	
	import com.moke.media.events.MediaEvent;
	
	public class MP3Controller extends MediaController {
	
		private var _savedTime:Number;
		private var _sound:Sound;
		private var _soundChannel:SoundChannel;
		private var _spectrumData:ByteArray;		

		/**
		* @description  Constructor.
		*/		
		override protected function init():void {
			super.init();
			_spectrumData = new ByteArray();
		}

		/**
		* @description  Handler for the SoundChannel's completion event.
		*
		* @param  pEvent  Event fired by SoundChannel.
		*/		
		private function onSoundComplete(pEvent:Event):void {
			stopMedia();
			dispatchEvent(new MediaEvent(MediaEvent.COMPLETE, mediaPosition, mediaDuration));
		}
	
		/**
		* @description  Handler for the completion event of the file's load.
		*
		* @param  pEvent  Event fired by Sound.
		*/		
		private function onSoundLoaded(pEvent:Event):void {
			mediaDuration = _sound.length;
			dispatchEvent(pEvent);
		}
	
		/**
		* @description  Handler for an error in loading sound file.
		*
		* @param  pEvent  Event fired by Sound.
		*/		
		private function onLoadError(pEvent:Event):void {
			dispatchEvent(pEvent);
		}
		
		/**
		* @description  Handler for load progress of sound file.
		*
		* @param  pEvent  Event fired by Sound.
		*/		
		private function onLoadProgress(pEvent:Event):void {
			dispatchEvent(pEvent);
		}
		
		/**
		* @description  Calls the play() method on the Sound instance and stores the resulting SoundChannel instance.
		*
		* @param  pStartTime  The time in milliseconds at which to start the sound's playback.
		*/
		private function playSound(pStartTime:Number):void {
			_soundChannel = _sound.play(pStartTime, 0, getSoundTransform());
			_soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete, false, 0, true);
		}
		
		/**
		* @description  Applies current sound settings to media. To be overridden.
		*/		
		override protected function applySoundTransform():void {
			_soundChannel.soundTransform = getSoundTransform();
		}
		
		/**
		* @description  Triggers the request and loading of the controller's media.
		*
		* @param  pFileURL  The http or rtmp path to the file to load.
		* @param  pDuration  The duration of the media that is being loaded (if known).
		*/		
		override public function loadMedia(pFileURL:String):void {
			super.loadMedia(pFileURL);
			_sound = new Sound();
            _sound.addEventListener(Event.COMPLETE, onSoundLoaded, false, 0, true);
            _sound.addEventListener(IOErrorEvent.IO_ERROR, onLoadError, false, 0, true);
            _sound.addEventListener(ProgressEvent.PROGRESS, onLoadProgress, false, 0, true);
			try {
				_sound.load(new URLRequest(mediaFile));
				startMedia();
			} catch (e:Error) {}
		}
	
		/**
		* @description  Begins playback of the loaded media.
		*/		
		override public function startMedia():void {
			playSound(0);
			super.startMedia();
		} 
	
		/**
		* @description  Sends the media to the specified position, in milliseconds.
		*
		* @param  pOffset  The position in milliseconds to send the media to.
		*/
		override public function seek(pOffset:Number):void {
			stopMedia();
			if (paused) {
				_savedTime = pOffset;
			} else {
				super.startMedia();
				playSound(pOffset);
			}
			super.seek(pOffset);
		} 
	
		/**
		* @description  Halts playback of media.
		*/		
		override public function stopMedia():void {
			super.stopMedia();
			_soundChannel.stop();
		} 
	
		/**
		* @description  Pauses or resumes playback of media.
		*
		* @param  pPause  True to pause media. False to resume.
		*/		
		override public function pauseMedia(pPause:Boolean):void {
			super.pauseMedia(pPause);
			if (paused) {
				_savedTime = mediaPosition;
				stopMedia();
			} else {
				super.startMedia();
				playSound(_savedTime);
			}
		} 

		/**
		* @description  Destructor cleans up controller.
		*/		
		override public function die():void {
			super.die();
			if (_sound != null) {
				try {
					_sound.close();
				} catch (e:Error) {}
			}
		}
		
		/**
		* @description  Returns the current position, in milliseconds, of the media.
		*
		* @returns  The current position, in milliseconds, of the media.
		*/		
		override public function get mediaPosition():Number {
			if (paused) {
				return _savedTime;
			} else {
				return _soundChannel.position;
			}
		}

		/**
		* @description  Computes the sound specturm for the current sounds in the player and returns the ByteArray.
		*
		* @param  pFFTMode  A Boolean value indicating whether a Fourier transformation is performed on the sound data first.
		* @param  pStretchFactor  The resolution of the sound samples.
		*/		
		public function getSoundSpectrum(pFFTMode:Boolean=true, pStretchFactor:int=0):ByteArray {
			SoundMixer.computeSpectrum(_spectrumData, pFFTMode, pStretchFactor);;
			return _spectrumData;
		}

		/**
		* @description  Returns the media playback object for the controller.
		*
		* @returns  A reference to the object containing the media.
		*/		
		override public function get media():Object { return _sound; }

	}
	
}